package com.ww.exercise.coroutine.mid.q16

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import kotlin.random.Random

// 自定义异常类型
class InsufficientResourcesException(message: String) : Exception(message)
class TaskFailedException(message: String, val originalException: Throwable? = null) : Exception(message)

// 任务执行状态记录
data class FaultToleranceRecord(
    val taskType: String,
    val eventType: String, // 降级，取消，重试，成功，失败
    val timestamp: String,
    val reason: String
)

// 容错报告生成器
class FaultToleranceReport {
    private val records = mutableListOf<FaultToleranceRecord>()
    private val mutex = Mutex()

    suspend fun addRecord(record: FaultToleranceRecord) {
        mutex.withLock {
            records.add(record)
        }
    }

    fun generateReport() {
        println("\n===== 数据清算容错报告 =====")
        println("报告生成时间: ${LocalDateTime.now().format(formatter)}")
        println("任务事件记录:")
        records.forEach { record ->
            println("${record.timestamp} [${record.taskType}] ${record.eventType}: ${record.reason}")
        }
    }

    companion object {
        private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
    }
}

// 订单金额核算（核心任务）
suspend fun calculateOrderAmount(
    report: FaultToleranceReport,
    simulateResourceIssue: Boolean = false
): Double {
    return withContext(Dispatchers.Default) {
        try {
            println("开始核心任务：订单金额核算...")

            // 模拟处理过程
            delay(3000)

            // 模拟资源不足的情况
            if (simulateResourceIssue && Random.nextDouble() < 0.3) {
                val errorMsg = "订单核算过程中检测到系统资源不足"
                report.addRecord(
                    FaultToleranceRecord(
                        "核心任务",
                        "资源不足",
                        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                        errorMsg
                    )
                )
                throw InsufficientResourcesException(errorMsg)
            }

            // 模拟计算结果
            val totalAmount = Random.nextDouble(5_000_000.0, 15_000_000.0)
            report.addRecord(
                FaultToleranceRecord(
                    "核心任务",
                    "成功",
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                    "订单金额核算完成，总金额：${String.format("%.2f", totalAmount)}元"
                )
            )
            totalAmount
        } catch (e: InsufficientResourcesException) {
            throw e
        } catch (e: Exception) {
            report.addRecord(
                FaultToleranceRecord(
                    "核心任务",
                    "失败",
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                    "订单金额核算失败：${e.message}"
                )
            )
            throw TaskFailedException("核心任务执行失败", e)
        }
    }
}

// 用户消费习惯关系（重要任务）
suspend fun analyzeUserConsumption(
    report: FaultToleranceReport,
    simplified: Boolean = false
): Boolean {
    return withContext(Dispatchers.Default) {
        try {
            println("开始${if (simplified) "简化版" else ""}重要任务：用户消费习惯分析")

            // 模拟处理过程，简化版耗时更少
            val processingTime = if (simplified) 1500L else 4000L
            delay(processingTime)

            // 模拟处理失败的情况
            if (Random.nextDouble() < (if (simplified) 0.1 else 0.3)) {
                val errorMsg = "${if (simplified) "简化版" else ""}用户消费习惯分析失败"
                report.addRecord(
                    FaultToleranceRecord(
                        "重要任务",
                        "失败",
                        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                        errorMsg
                    )
                )
                throw TaskFailedException(errorMsg)
            }

            report.addRecord(
                FaultToleranceRecord(
                    "重要任务",
                    "成功",
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                    "${if (simplified) "简化版" else ""}用户消费习惯分析完成"
                )
            )
            true
        } catch (e: Exception) {
            throw e
        }
    }
}

// 日志冗余备份（非核心任务）
suspend fun backupRedundantLogs(
    report: FaultToleranceReport,
    cancellationCheck: CancellableContinuation<Unit>? = null
): Boolean {
    return withContext(Dispatchers.IO) {
        try {
            println("开始非核心任务：日志冗余备份...")

            // 模拟分阶段处理，便于在取消时能记录进度
            for (i in 1..5) {
                delay(1000)
                println("日志备份进度：${i * 20}%")

                // 检查是否需要取消
                if (isActive.not()) {
                    throw CancellationException("日志备份任务被取消")
                }
            }

            report.addRecord(
                FaultToleranceRecord(
                    "非核心任务",
                    "成功",
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                    "日志冗余备份完成"
                )
            )
            true
        } catch (e: CancellationException) {
            report.addRecord(
                FaultToleranceRecord(
                    "非核心任务",
                    "取消",
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                    e.message ?: "日志备份被取消"
                )
            )
            false
        } catch (e: Exception) {
            report.addRecord(
                FaultToleranceRecord(
                    "非核心任务",
                    "失败",
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                    "日志备份失败: ${e.message}"
                )
            )
            false
        }
    }
}

// 主清算任务协调器
suspend fun coordinateSettlementTasks(simulateResourceIssues: Boolean = true) {
    // 创建带SupervisorJob的协程作用域，实现子任务异常隔离
    val supervisorScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
    val report = FaultToleranceReport()

    try {
        // 跟踪非核心任务的Job，便于需要时取消
        var backupJob: Job? = null

        // 启动核心任务
        val coreJob = supervisorScope.async {
            calculateOrderAmount(report, simulateResourceIssues)
        }

        // 启动重要任务（带重试逻辑）
        val importantJob = supervisorScope.async {
            try {
                // 首次尝试完整分析
                analyzeUserConsumption(report, simplified = false)
            } catch (e: Exception) {
                // 失败后记录并尝试简化版
                report.addRecord(
                    FaultToleranceRecord(
                        "重要任务",
                        "重试",
                        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                        "完整分析失败，尝试简化版分析：${e.message}"
                    )
                )
                // 重试简化版
                analyzeUserConsumption(report, simplified = true)
            }
        }

        // 启动非核心任务
        backupJob = supervisorScope.launch {
            backupRedundantLogs(report)
        }

        // 等待核心任务完成，监听资源不足异常
        try {
            val totalAmount = coreJob.await()
            println("核心任务完成：总订单金额 = ${String.format("%.2f", totalAmount)}元")
        } catch (e: InsufficientResourcesException) {
            // 核心任务资源不足时，取消非核心任务释放资源
            println("检测到资源不足，取消非核心任务释放资源...")
            backupJob.cancel(CancellationException("核心任务资源不足，需要释放系统资源"))
        }

        // 等待重要任务完成
        val importantTaskResult = importantJob.await()
        println("重要任务${if (importantTaskResult) "成功" else "失败"}：用户消费习惯分析")

        // 等待非核心任务完成（可能已被取消）
        backupJob.join()
    } finally {
        // 取消所有子任务，生成报告
        supervisorScope.cancel()
        report.generateReport()
    }
}

fun main() = runBlocking {
    println("===== 大促后数据清算系统启动 =====")
    println("启动时间: ${LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))}")

    coordinateSettlementTasks(simulateResourceIssues = true)

    println("\n===== 数据清算系统完成 =====")
}